package no.uib.hplims.views.orders;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.TextField;
import com.vaadin.ui.TwinColSelect;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.Runo;

import no.uib.hplims.models.Rule;
import no.uib.hplims.models.RuleFactory;
import no.uib.hplims.models.RuleFactory.FLAG_TYPE;
import no.uib.hplims.models.RuleFactory.POSITION;
import no.uib.hplims.models.RuleFactory.RULE_META;
import no.uib.hplims.models.RuleFactory.RULE_TYPE;

public class RuleCustomizer extends Window implements
		Property.ValueChangeListener {

	private static final long serialVersionUID = 4038446415833009747L;

	private RuleFactory ruleFactory = null;

	private VerticalLayout layout = null;
	private GridLayout grid = null;

	private Button createNewRuleButton = null;
	private Button okButton = null;

	private TwinColSelect rules = null;

	private OptionGroup flagSelect = null;
	private OptionGroup ruleSelect = null;
	private OptionGroup positionSelect = null;
	private TextField ruleName = null;
	private TextField minLength = null;
	private TextField maxLength = null;
	private TextField aminoAcid = null;
	private TextField maxOccurences = null;

	private static final List<String> flagTypes = Arrays.asList(new String[] {
			"Error", "Warning" });
	private static final List<String> ruleTypes = Arrays.asList(new String[] {
			"Length", "Allow", "Disallow" });
	private static final List<String> positions = Arrays.asList(new String[] {
			"Entire sequence", "N-terminal", "C-terminal" });

	public RuleCustomizer(RuleFactory ruleFactory) {
		super("Select rules to apply");
		setWidth("600px");
		center();
		this.setModal(true);
		this.ruleFactory = ruleFactory;

		layout = new VerticalLayout();
		layout.setSpacing(true);
		layout.setMargin(true);

		Label header = new Label("Select Rules");
		header.setStyleName(Runo.LABEL_H2);

		rules = new TwinColSelect();
		rules.setImmediate(true);
		rules.setSizeFull();
		rules.setLeftColumnCaption("Inactive Rules");
		rules.setRightColumnCaption("Active Rules");
		// Add active rules
		for (Rule r : ruleFactory.getActiveRules()) {
			rules.addItem(r);
			rules.select(r);
		}
		// Add inactive rules
		for (Rule r : ruleFactory.getInactiveRules()) {
			rules.addItem(r);
		}
		rules.addListener(this);
		layout.addComponent(header);
		layout.addComponent(rules);

		layout.addComponent(createNewRuleForm());
		setContent(layout);
	}

	private VerticalLayout createNewRuleForm() {
		VerticalLayout vl = new VerticalLayout();
		// Create layout (3x5 GridLayout)
		grid = new GridLayout(3, 5);
		grid.setMargin(false, false, true, false);
		grid.setSpacing(true);
		// Add grid to layout
		vl.addComponent(grid);

		Label header = new Label("Create New Rule");
		header.setStyleName(Runo.LABEL_H2);

		ruleName = new TextField();
		ruleName.setInputPrompt("Give the rule a name");
		ruleName.setImmediate(true);
		ruleName.setWidth("100%");

		flagSelect = new OptionGroup("Warning Level", flagTypes);
		flagSelect.setDescription("Tooltip");
		flagSelect.setNullSelectionAllowed(false); // user can not unselect
		flagSelect.select("Error"); // select this by default
		flagSelect.setImmediate(true); // send changes to server immediately
		flagSelect.addListener(this);

		ruleSelect = new OptionGroup("Rule Type", ruleTypes);
		ruleSelect.setNullSelectionAllowed(false); // user can not unselect
		ruleSelect.select("Length"); // select this by default
		ruleSelect.setImmediate(true); // send changes to server immediately
		ruleSelect.addListener(this);

		positionSelect = new OptionGroup("Position", positions);
		positionSelect.setNullSelectionAllowed(false);
		positionSelect.select("Entire Sequence"); // select this by default
		positionSelect.setImmediate(true); // send changes to server immediately
		positionSelect.addListener(this);

		minLength = new TextField();
		minLength.setCaption("Min Sequence Length");
		minLength.setInputPrompt("Min Sequence Length");
		minLength.setImmediate(true); // send changes to server immediately

		maxLength = new TextField();
		maxLength.setCaption("Max Sequence Length");
		maxLength.setInputPrompt("Max Sequence Length");
		maxLength.setImmediate(true); // send changes to server immediately

		aminoAcid = new TextField();
		aminoAcid.setCaption("Amino Acid");
		aminoAcid.setInputPrompt("Amino Acid");
		aminoAcid.setImmediate(true); // send changes to server immediately
		aminoAcid.setEnabled(false); // is disabled by default
		aminoAcid.setMaxLength(1);

		maxOccurences = new TextField();
		maxOccurences.setCaption("Max Occurences");
		maxOccurences.setInputPrompt("Max Occurences");
		maxOccurences.setImmediate(true); // send changes to server immediately
		maxOccurences.setEnabled(false); // is disabled by default

		// Add all components to grid
		grid.addComponent(header, 0, 0, 2, 0);
		grid.addComponent(ruleName, 0, 1, 2, 1);
		grid.addComponent(flagSelect, 0, 2);
		grid.addComponent(ruleSelect, 1, 2);
		grid.addComponent(positionSelect, 2, 2);
		grid.addComponent(minLength, 0, 3);
		grid.addComponent(maxLength, 1, 3);
		grid.addComponent(aminoAcid, 0, 4);
		grid.addComponent(maxOccurences, 1, 4);

		// Create and add buttons to layout
		createNewRuleButton = new Button("Create Rule");
		createNewRuleButton.addListener(clickListener);

		okButton = new Button("OK");
		okButton.addListener(clickListener);

		HorizontalLayout hl = new HorizontalLayout();
		hl.setSizeFull();
		hl.addComponent(createNewRuleButton);
		hl.addComponent(okButton);
		hl.setComponentAlignment(createNewRuleButton, Alignment.BOTTOM_LEFT);
		hl.setComponentAlignment(okButton, Alignment.BOTTOM_RIGHT);

		vl.addComponent(hl);
		return vl;
	}

	public void valueChange(ValueChangeEvent event) {
		if (event.getProperty().toString() == "Length") {
			minLength.setEnabled(true);
			maxLength.setEnabled(true);
			aminoAcid.setEnabled(false);
			maxOccurences.setEnabled(false);
			positionSelect.select("Entire Sequence");
			positionSelect.setItemEnabled("N-terminal", false);
			positionSelect.setItemEnabled("C-terminal", false);
		} else if (event.getProperty().toString() == "Allow") {
			minLength.setEnabled(false);
			maxLength.setEnabled(false);
			aminoAcid.setEnabled(true);
			maxOccurences.setEnabled(true);
			positionSelect.setItemEnabled("N-terminal", true);
			positionSelect.setItemEnabled("C-terminal", true);
		} else if (event.getProperty().toString() == "Disallow") {
			minLength.setEnabled(false);
			maxLength.setEnabled(false);
			aminoAcid.setEnabled(true);
			maxOccurences.setEnabled(true);
			positionSelect.setItemEnabled("N-terminal", true);
			positionSelect.setItemEnabled("C-terminal", true);
		} else if (event.getProperty().getClass().equals(TwinColSelect.class)) {
			// Get the set of currently activated rules
			Set<Rule> active = (Set<Rule>) event.getProperty().getValue();

			// Deactivate rules that should be inactive
			Iterator<Rule> it = ruleFactory.getActiveRules().iterator();
			while (it.hasNext()) {
				Rule r = it.next();
				if (!active.contains(r)) {
					it.remove();
					ruleFactory.deactivateRule(r);
				}
			}

			// Activate rules that should be active
			it = ruleFactory.getInactiveRules().iterator();
			while (it.hasNext()) {
				Rule r = it.next();
				if(active.contains(r)) {
					it.remove();
					ruleFactory.activateRule(r);
				}
			}
		}

	}

	private ClickListener clickListener = new ClickListener() {

		private static final long serialVersionUID = 2392859159560055956L;

		public void buttonClick(ClickEvent event) {
			if (event.getButton() == createNewRuleButton) {
				FLAG_TYPE ft = null;
				RULE_TYPE ruleType = null;
				POSITION position = null;
				RULE_META meta = null;
				String givenName = null;
				String aa = null;
				int maxOccur = -1;
				int minimumLength = -1;
				int maximumLength = -1;

				// Determine warning level of the rule
				if (flagSelect.isSelected("Error")) {
					ft = FLAG_TYPE.RED;
				} else if (flagSelect.isSelected("Warning")) {
					ft = FLAG_TYPE.YELLOW;
				}
				// Determine the rule type
				if (ruleSelect.isSelected("Length")) {
					ruleType = RULE_TYPE.LENGTH;
					minimumLength = Integer.parseInt((String) minLength
							.getValue());
					maximumLength = Integer.parseInt((String) maxLength
							.getValue());
				} else if (ruleSelect.isSelected("Allow")) {
					ruleType = RULE_TYPE.ALLOW;
					aa = (String) aminoAcid.getValue();
				} else if (ruleSelect.isSelected("Disallow")) {
					ruleType = RULE_TYPE.DISALLOW;
					aa = (String) aminoAcid.getValue();
				}
				// Determine position
				if (positionSelect.isSelected("N-terminal")) {
					position = POSITION.N_TERMINAL;
					maxOccur = 1;
				} else if (positionSelect.isSelected("C-terminal")) {
					position = POSITION.C_TERMINAL;
					maxOccur = 1;
				} else if (positionSelect.isSelected("Entire Sequence")) {
					position = POSITION.SEQUENCE;
					// set maxOccurences for entire sequence only when rule type
					// is not length
					if (ruleType != RULE_TYPE.LENGTH) {
						maxOccur = Integer.parseInt((String) maxOccurences
								.getValue());
					}
				}
				// Set rule meta, always custom.
				meta = RULE_META.CUSTOM;
				givenName = (String) ruleName.getValue();

				Rule r = ruleFactory.createNewRule(ft, ruleType, position,
						meta, givenName, aa, maxOccur, minimumLength,
						maximumLength);
				rules.addItem(r);
				rules.select(r);

			} else if (event.getButton() == okButton) {
				close();
			}
		}
	};

}
